﻿using System;
using System.Globalization;
using GeneticAlgorithms.Genomes;
using GeneticAlgorithms.Tools;

namespace GeneticAlgorithms.Operators.Crossover
{
    /// <summary>
    /// Base class for crossover operators.
    /// </summary>
    /// <typeparam name="TGenome">The type of the genome.</typeparam>
    /// <typeparam name="TGene">The type of the gene.</typeparam>
    public abstract class BaseCrossoverOperator<TGenome, TGene> : IGeneticCrossover<TGenome, TGene>
        where TGenome : IGenome<TGene>
    {
        #region IGeneticCrossover<TGene> Members

        /// <summary>
        /// Gets the cardinality of the crossover.
        /// </summary>
        /// <value>
        /// The cardinality of the crossover
        /// operation, that is the number of expected
        /// parents to be provided, as well as the number
        /// of resulting children.
        /// </value>
        public virtual int Cardinality
        {
            get { return 2; }
        }

        /// <summary>
        /// Crosses the specified parents.
        /// </summary>
        /// <param name="parents">The parents.</param>
        /// <param name="children">The children.</param>
        public virtual void Cross(TGenome[] parents, TGenome[] children)
        {
            this.ChechCardinality(parents, children);

            bool straightCopy = true;

            int max = parents[0].Length;
            this.Init(max);

            TraceHelper.WriteLine("Crossover:");
            TraceHelper.Write("Cut points: ");
            for (int i = 0; i < max; ++i)
            {
                if (this.ShouldSwitch(i))
                {
                    straightCopy = !straightCopy;
                    TraceHelper.Write(i);
                }

                if (straightCopy)
                {
                    children[0][i] = parents[0][i];
                    children[1][i] = parents[1][i];
                }
                else
                {
                    children[1][i] = parents[0][i];
                    children[0][i] = parents[1][i];
                }
            }

            TraceHelper.WriteLine(String.Empty);
            TraceHelper.WriteLine("p1: {0}", parents[0]);
            TraceHelper.WriteLine("p2: {0}", parents[1]);
            TraceHelper.WriteLine("c1: {0}", children[0]);
            TraceHelper.WriteLine("c2: {0}", children[1]);
        }

        /// <summary>
        /// Cheches the cardinality of the provided
        /// parents and children arrays.
        /// </summary>
        /// <param name="parents">The parents array.</param>
        /// <param name="children">The children array.</param>
        protected void ChechCardinality(TGenome[] parents, TGenome[] children)
        {
            if ((parents.Length != this.Cardinality) || (children.Length != this.Cardinality))
            {
                throw new ArgumentException(
                    String.Format(
                        CultureInfo.CurrentCulture,
                        "Wrong cardinality for the parents or children array. Expected length: {0}\nReceived length:\n parents:{1}\nchildren:{2}",
                        this.Cardinality,
                        parents.Length,
                        children.Length));
            }
        }

        /// <summary>
        /// Determines whether a switch should happen in the specified position.
        /// </summary>
        /// <param name="currentPos">The current position.</param>
        /// <returns>
        /// <c>true</c> in case the operator should switch the
        /// order in which it is copying genes, <c>false</c>
        /// otherwise.
        /// </returns>
        protected abstract bool ShouldSwitch(int currentPos);

        /// <summary>
        /// Allows inheriting classes to perform their
        /// own initialization, prisor to crossover beginning.
        /// </summary>
        /// <param name="maximumIndex">
        /// The maxValue allowed index inside the genomes.
        /// </param>
        protected virtual void Init(int maximumIndex)
        {
        }
        
        #endregion
    }
}
